/**
 * 
 */
package com.finalministry.db.logger;

import org.apache.log4j.LogManager;
import org.apache.log4j.MDC;

import com.finalministry.db.message.MessageFormatter;
import com.finalministry.db.style.CustomStringStyle;

/**
 * @author simunine
 */
public class Logger {

	private static org.apache.log4j.Logger logger;

	public Logger(Class clazz) {

		this(clazz.getName(), null);
	}

	public Logger(Class clazz, String userId) {

		this(clazz.getName(), userId);
	}

	public Logger(String name, String userId) {

		logger = LogManager.getLogger(name);

		MDC.put("clazz", name);

		if (userId != null)
			MDC.put("userId", userId);
	}

	private void dispatchToLogLevel(LogLevel level, Throwable t, String formattedMessage) {

		if (level.equals(LogLevel.CONFIG)) {
			logger.info(formattedMessage, t);
		} else if (level.equals(LogLevel.DURATION)) {
			logger.trace(formattedMessage, t);
		} else if (level.equals(LogLevel.ENTER)) {
			logger.trace(formattedMessage, t);
		} else if (level.equals(LogLevel.EXIT)) {
			logger.trace(formattedMessage, t);
		} else if (level.equals(LogLevel.FINE)) {
			logger.debug(formattedMessage, t);
		} else if (level.equals(LogLevel.FINER)) {
			logger.debug(formattedMessage, t);
		} else if (level.equals(LogLevel.FINEST)) {
			logger.trace(formattedMessage, t);
		} else if (level.equals(LogLevel.TRACE)) {
			logger.debug(formattedMessage, t);
		} else if (level.equals(LogLevel.DEBUG)) {
			logger.debug(formattedMessage, t);
		} else if (level.equals(LogLevel.INFO)) {
			logger.info(formattedMessage, t);
		} else if (level.equals(LogLevel.WARN)) {
			logger.warn(formattedMessage, t);
		} else if (level.equals(LogLevel.ERROR)) {
			logger.error(formattedMessage, t);
		} else if (level.equals(LogLevel.FATAL)) {
			logger.error(formattedMessage, t);
		}
	}

	private void logIt(LogLevel level, Object message, Throwable t, Object... arguments) {

		String formattedMessage = formatMsg(message, arguments);
		dispatchToLogLevel(level, t, formattedMessage);
	}

	private StringBuffer withCR(Object message, Object arguments[]) {

		return new StringBuffer().append(MessageFormatter.replaceArguments(message, arguments));
	}

	private StringBuffer withCR(Object message) {

		return new StringBuffer().append(message);
	}

	private String formatMsg(Object message, Object[] arguments) {

		if (arguments != null && arguments.length > 0) {
			return withCR(message, arguments).toString();
		} else {
			return withCR(message).toString();
		}
	}

	private String formatForLog(Object obj) {

		if (obj == null) {
			return "null";
		}
		return CustomStringStyle.customToString(obj);
	}

	public void error(Object message, Object[] arguments, Throwable t) {

		logIt(LogLevel.ERROR, message, t, arguments);
	}

	public void error(Object message, Throwable t, Object... arguments) {

		logIt(LogLevel.ERROR, message, t, arguments);
	}

	public void error(Object message, Object... arguments) {

		logIt(LogLevel.ERROR, message, null, arguments);
	}

	public void error(Object message, Throwable t) {

		logIt(LogLevel.ERROR, message, t);
	}

	public void error(Object message) {

		logIt(LogLevel.ERROR, message, null);
	}

	public void warn(Object message, Throwable t, Object... arguments) {

		logIt(LogLevel.WARN, message, t, arguments);
	}

	public void warn(Object message, Object[] arguments, Throwable t) {

		logIt(LogLevel.WARN, message, t, arguments);
	}

	public void warn(Object message, Object... arguments) {

		logIt(LogLevel.WARN, message, null, arguments);
	}

	public void warn(Object message, Throwable t) {

		logIt(LogLevel.WARN, message, t);
	}

	public void warn(Object message) {

		logIt(LogLevel.WARN, message, null);
	}

	public void info(Object message, Object[] arguments, Throwable t) {

		if (logger.isInfoEnabled()) {
			logIt(LogLevel.INFO, message, t, arguments);
		}
	}

	public void info(Object message, Throwable t, Object... arguments) {

		if (logger.isInfoEnabled()) {
			logIt(LogLevel.INFO, message, t, arguments);
		}
	}

	public void info(Object message, Object... arguments) {

		if (logger.isInfoEnabled()) {
			logIt(LogLevel.INFO, message, null, arguments);
		}
	}

	public void info(Object message, Throwable t) {

		if (logger.isInfoEnabled()) {
			logIt(LogLevel.INFO, message, t);
		}
	}

	public void info(Object message) {

		if (logger.isInfoEnabled()) {
			logIt(LogLevel.INFO, message, null);
		}
	}

	public void fine(Object message, Object... arguments) {

		if (logger.isDebugEnabled()) {
			logIt(LogLevel.FINE, message, null, arguments);
		}
	}

	public void finest(String message, Object... arguments) {

		if (logger.isDebugEnabled()) {
			logIt(LogLevel.FINEST, message, null, arguments);
		}
	}

	public void finest(Object message, Object[] arguments, Throwable t) {

		if (logger.isDebugEnabled()) {
			logIt(LogLevel.FINEST, message, t, arguments);
		}
	}

	public void finest(Object message, Throwable t, Object... arguments) {

		if (logger.isDebugEnabled()) {
			logIt(LogLevel.FINEST, message, t, arguments);
		}
	}

	public void trace(String message, Object... arguments) {

		if (logger.isTraceEnabled()) {
			logIt(LogLevel.TRACE, message, null, arguments);
		}
	}

	public void trace(Object message, Object[] arguments, Throwable t) {

		if (logger.isTraceEnabled()) {
			logIt(LogLevel.TRACE, message, t, arguments);
		}
	}

	public void trace(Object message, Throwable t, Object... arguments) {

		if (logger.isTraceEnabled()) {
			logIt(LogLevel.TRACE, message, t, arguments);
		}
	}

	public void fine(Object message, Throwable t, Object... arguments) {

		if (logger.isTraceEnabled()) {
			logIt(LogLevel.FINE, message, t, arguments);
		}
	}

	public void fine(Object message) {

		if (logger.isDebugEnabled()) {
			logIt(LogLevel.FINE, message, null);
		}
	}

	public void fine(Object message, Throwable t) {

		if (logger.isDebugEnabled()) {
			logIt(LogLevel.FINE, message, t);
		}
	}

	public void finer(Object message, Object... arguments) {

		if (logger.isDebugEnabled()) {
			logIt(LogLevel.FINER, message, null, arguments);
		}
	}

	public void finer(Object message, Object[] arguments, Throwable t) {

		if (logger.isDebugEnabled()) {
			logIt(LogLevel.FINER, message, t, arguments);
		}
	}

	public void finer(Object message, Throwable t, Object... arguments) {

		if (logger.isDebugEnabled()) {
			logIt(LogLevel.FINER, message, t, arguments);
		}
	}

	public void finer(Object message) {

		if (logger.isDebugEnabled()) {
			logIt(LogLevel.FINER, message, null);
		}
	}

	public void finer(Object message, Throwable t) {

		if (logger.isDebugEnabled()) {
			logIt(LogLevel.FINER, message, t);
		}
	}

	private String getCalleeMethodName() {

		StackTraceElement[] traces = Thread.currentThread().getStackTrace();
		if (traces.length > 3) {
			StackTraceElement ste = traces[3];
			return ste.getMethodName();
		}
		return null;
	}

	// public Duration startDuration(Object arguments) {
	//
	// Slf4JStopWatch loggingStopWatch = new Slf4JStopWatch();
	// loggingStopWatch.start(arguments.toString());
	// long start = System.currentTimeMillis();
	// if (logger.isTraceEnabled()) {
	// final StringBuffer sb = new StringBuffer("starttime (");
	// sb.append(start);
	// sb.append(")-(");
	// sb.append(formatForLog(arguments));
	// sb.append(")");
	// logIt(LogLevel.DURATION, sb, null);
	// return new de.simnode.mda.logger.Duration(start, sb.toString(),
	// loggingStopWatch);
	// }
	// return new Duration(start, "", loggingStopWatch);
	// }

	public Duration stopDuration(Duration duration) {

		duration.stop();
		if (logger.isTraceEnabled()) {
			final StringBuffer sb = new StringBuffer("  stopTime (");
			sb.append(duration.getStopTime());
			sb.append(") ");

			sb.append("duration (");
			sb.append(duration.getDurationTime());
			sb.append(")");

			StringBuffer resultStringBuffer = new StringBuffer(duration.getMessage());
			resultStringBuffer.append(sb);
			duration.setMessage(resultStringBuffer.toString());
			logIt(LogLevel.DURATION, resultStringBuffer, null);
		}
		return duration;
	}

	// public Duration startDuration(Object... arguments) {
	//
	// Slf4JStopWatch loggingStopWatch = new Slf4JStopWatch();
	// loggingStopWatch.start(arguments.toString());
	// long start = System.currentTimeMillis();
	// if (logger.isTraceEnabled()) {
	// final StringBuffer sb = new StringBuffer().append("starttime (");
	// sb.append(start);
	// sb.append(")-(");
	// for (int i = 0; i < arguments.length; i++) {
	// if (i > 0) {
	// sb.append(", ");
	// }
	// sb.append(String.valueOf(arguments[i]));
	// }
	// sb.append(")");
	// logIt(LogLevel.DURATION, sb, null);
	// return new Duration(start, sb.toString(), loggingStopWatch);
	// }
	// return new Duration(start, "", loggingStopWatch);
	// }

	public void entering(Object... arguments) {

		if (logger.isTraceEnabled()) {
			final StringBuffer sb = new StringBuffer().append("entering ");
			String methodName = getCalleeMethodName();
			if (methodName != null) {
				sb.append(methodName);
				sb.append(" ");
			}
			sb.append("(");
			if (arguments != null) {
				for (int i = 0; i < arguments.length; i++) {
					if (i > 0) {
						sb.append(", ");
					}
					sb.append(String.valueOf(arguments[i]));
				}
			}
			sb.append(")");
			logIt(LogLevel.ENTER, sb, null);
		}
	}

	public void exiting() {

		if (logger.isTraceEnabled()) {
			logIt(LogLevel.EXIT, "exiting", null);
		}
	}

	public void exiting(Object result) {

		if (logger.isTraceEnabled()) {
			final StringBuffer sb = new StringBuffer("exiting with result (");
			sb.append(formatForLog(result));
			sb.append(")");
			logIt(LogLevel.EXIT, sb, null);
		}
	}

	public void exiting(Object... arguments) {

		if (logger.isTraceEnabled()) {
			final StringBuffer sb = new StringBuffer().append("exiting with result (");
			if (arguments != null) {
				for (int i = 0; i < arguments.length; i++) {
					if (i > 0) {
						sb.append(", ");
					}
					sb.append(formatForLog(arguments[i]));
				}
			}
			sb.append(")");
			logIt(LogLevel.EXIT, sb, null);
		}
	}

	public void debug(Object message, Throwable t) {

		if (logger.isDebugEnabled()) {
			logIt(LogLevel.DEBUG, message, t);
		}
	}

	public void debug(Object message, Object[] arguments, Throwable t) {

		if (logger.isDebugEnabled()) {
			logIt(LogLevel.DEBUG, message, t, arguments);
		}
	}

	public void debug(Object message, Throwable t, Object... arguments) {

		if (logger.isDebugEnabled()) {
			logIt(LogLevel.DEBUG, message, t, arguments);
		}
	}

	public void debug(Object message) {

		if (logger.isDebugEnabled()) {
			logIt(LogLevel.DEBUG, message, null);
		}
	}

	public void debug(Object message, Object... arguments) {

		if (logger.isDebugEnabled()) {
			logIt(LogLevel.DEBUG, message, null, arguments);
		}
	}

	/**
	 * @param message
	 *            Message
	 * @param argument
	 *            argument for replace in message
	 */
	public void debug(Object message, Object argument) {

		if (logger.isDebugEnabled()) {
			logIt(LogLevel.DEBUG, message, null, argument);
		}
	}

	public void finest(Object message) {

		if (logger.isTraceEnabled()) {
			logIt(LogLevel.FINEST, message, null);
		}
	}

	public void finest(Object message, Throwable t) {

		if (logger.isTraceEnabled()) {
			logIt(LogLevel.FINEST, message, t);
		}
	}

	public void trace(Object message, Throwable t) {

		if (logger.isTraceEnabled()) {
			logIt(LogLevel.FINEST, message, t);
		}
	}

	public void trace(Object message) {

		if (logger.isTraceEnabled()) {
			logIt(LogLevel.FINEST, message, null);
		}
	}
}
